#pragma once

struct CDeviceProperty
{
	PROPERTYKEY	*m_key;
	PROPVARIANT	m_value;

	CDeviceProperty()
	{
		m_key = NULL;
		PropVariantInit(&m_value);
	}

	template <typename TVal>
	CDeviceProperty(REFPROPERTYKEY key, const TVal& val)
	{
		//
		// save the key
		//
		m_key = const_cast<PROPERTYKEY *>(&key);

		//
		// copy the value to "m_value"; this will work
		// only if we have already defined a conversion
		// operator from the source type to the target
		// type
		//
		PropVariantInit(&m_value);
		*this = val;
	}

	CDeviceProperty(REFPROPERTYKEY key, const PROPVARIANT &val)
	{
		m_key = const_cast<PROPERTYKEY*>(&key);
		PropVariantInit(&m_value);
		PropVariantCopy(&m_value, &val);
	}

	CDeviceProperty(const CDeviceProperty& prop)
	{
		m_key = prop.m_key;
		PropVariantInit(&m_value);
		PropVariantCopy(&m_value, &prop.m_value);
	}

	~CDeviceProperty()
	{
		PropVariantClear(&m_value);
	}

	//
	// overload assignment operator to convert from common
	// datatypes to PROPVARIANT
	//
	void operator= (REFGUID val)
	{
		PropVariantClear(&m_value);
		InitPropVariantFromCLSID(val, &m_value);
	}

	void operator= (const BOOL val)
	{
		PropVariantClear(&m_value);
		InitPropVariantFromBoolean(val, &m_value);
	}

	void operator= (const FLOAT val)
	{
		PropVariantClear(&m_value);
		V_VT(&m_value) = VT_R4;
		V_R4(&m_value) = val;
	}

	void operator= (const DOUBLE val)
	{
		PropVariantClear(&m_value);
		InitPropVariantFromDouble(val, &m_value);
	}

	void operator= (const IUnknown *pUnk)
	{
		PropVariantClear(&m_value);
		V_VT(&m_value) = VT_UNKNOWN;
		V_UNKNOWN(&m_value) = const_cast<IUnknown *>(pUnk);
	}

	void operator= (const LONG val)
	{
		PropVariantClear(&m_value);
		InitPropVariantFromInt32(val, &m_value);
	}

	void operator= (const LONGLONG val)
	{
		PropVariantClear(&m_value);
		InitPropVariantFromInt64(val, &m_value);
	}

	void operator= (const wstring& val)
	{
		PropVariantClear(&m_value);
		InitPropVariantFromString(val.c_str(), &m_value);
	}

	void operator= (const LPWSTR val)
	{
		PropVariantClear(&m_value);
		InitPropVariantFromString(val, &m_value);
	}

	void operator= (const ULONG val)
	{
		PropVariantClear(&m_value);
		InitPropVariantFromUInt32(val, &m_value);
	}

	void operator= (const ULONGLONG val)
	{
		PropVariantClear(&m_value);
		InitPropVariantFromUInt64(val, &m_value);
	}

	void operator= (const FILETIME val)
	{
		PropVariantClear(&m_value);
		InitPropVariantFromFileTime(&val, &m_value);
	}

	//
	// overload type conversion operators to convert from
	// PROPVARIANT to common datatypes
	//
	operator GUID()
	{
		GUID guid = {0};
		PropVariantToGUID(m_value, &guid);
		return guid;
	}

	operator BOOL()
	{
		return PropVariantToBooleanWithDefault(m_value, FALSE);
	}

	operator FLOAT()
	{
		return V_R4(&m_value);
	}

	operator DOUBLE()
	{
		return PropVariantToDoubleWithDefault(m_value, 0.0);
	}

	operator IUnknown*()
	{
		return V_UNKNOWN(&m_value);
	}

	operator LONG()
	{
		return PropVariantToInt32WithDefault(m_value, 0);
	}

	operator LONGLONG()
	{
		return PropVariantToInt64WithDefault(m_value, 0);
	}

	operator LPWSTR()
	{
		//
		// NOTE:
		//	Caller is expected to free the memory if it is non-NULL
		//	with "CoTaskMemFree".
		//
		PWSTR pszStr = NULL;
		HRESULT hr = PropVariantToStringAlloc(m_value, &pszStr);
		if(SUCCEEDED(hr))
			return pszStr;
		return NULL;
	}

	operator wstring()
	{
		LPWSTR pszStr = *this;
		wstring str(pszStr);
		CoTaskMemFree(pszStr);
		return str;
	}

	operator ULONG()
	{
		return PropVariantToUInt32WithDefault(m_value, 0);
	}

	operator ULONGLONG()
	{
		return PropVariantToUInt64WithDefault(m_value, 0);
	}

	operator FILETIME()
	{
		FILETIME ft = { 0 };
		PropVariantToFileTime(m_value, PSTF_UTC, &ft);
		return ft;
	}
};
